/*
 * Copyright (C) 2010,2011  Paulo Cesar Pereira de Andrade.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Authors:
 *	Paulo Cesar Pereira de Andrade
 */

/*
 * Prototypes
 */
#if PROTO
static void
fp_atanh_fp(f_t *u, f_t *v, eint32_t m);

static void
dp_atanh_dp(d_t *u, d_t *v, eint32_t m);

static void
vo_atanh_vo__d(vo_t *u, vo_t *v, eint32_t m);

static void
vo_atanh_vo__r(vo_t *u, vo_t *v, eint32_t m);
#endif

/*
 * Implementation
 */
#if CODE
void
evector_atanh(evector_t *v, etype_t t)
{
    eint32_t	 l;
    evector_t	*r;
    etype_t	 rt;
    v_enter();

    v_check(2);
    l = v->length;

    rt = match(t, t, 0, 1);
    if (rt != t)
	v = coerce(v, t, rt);
    r = create(v, rt);

    switch (rt) {
	case t_float32:
	    fp_atanh_fp(r->v.obj, v->v.obj, l);
	    break;
	case t_float64:
	    dp_atanh_dp(r->v.obj, v->v.obj, l);
	    break;
	default:
	    if (!thread_self->flt)
		vo_atanh_vo__d(r->v.obj, v->v.obj, l);
	    else
		vo_atanh_vo__r(r->v.obj, v->v.obj, l);
	    break;
    }
    v_leave();
}

static void
fp_atanh_fp(f_t *u, f_t *v, eint32_t m)
{
    for (; m; m--, u++, v++) {
	if (likely(!isnan(*v))) {
	    if (likely(fabs(*v) < 1.0))
		*u = atanhf(*v);
	    else
		eraise_exception(except_floating_point_error);
	}
	else
	    *u = NAN;
    }
}

static void
dp_atanh_dp(d_t *u, d_t *v, eint32_t m)
{
    for (; m; m--, u++, v++) {
	if (likely(!isnan(*v))) {
	    if (likely(fabs(*v) < 1.0))
		*u = atanh(*v);
	    else
		eraise_exception(except_floating_point_error);
	}
	else
	    *u = NAN;
    }
}

static void
vo_atanh_vo__d(vo_t *u, vo_t *v, eint32_t m)
{
    for (; m; u++, v++, m--) {
	switch (eobject_type(*v)) {
	    case t_void:
		set_d(ed_atanh_d(0.0));
		break;
	    case t_int:
		if (get_i(v) > -1 && get_i(v) < 1)
		    set_d(ed_atanh_i(get_i(v)));
		else
		    set_dd(edd_atanh_i(get_i(v)));
		break;
	    case t_float:
		if (likely(!isnan(get_d(v)))) {
		    if (fabs(get_d(v)) < 1.0)
			set_d(ed_atanh_d(get_d(v)));
		    else
			set_dd(edd_atanh_d(get_d(v)));
		}
		else
		    set_d(NAN);
		break;
	    case t_mpz:
		set_dd(edd_atanh_z(get_z(v)));
		break;
	    case t_mpq:
		if ((mpq_sgn(get_q(v)) >= 0 &&
		     mpq_cmp_ui(get_q(v), 1, 1) < 0) ||
		    (mpq_sgn(get_q(v)) < 0 &&
		     mpq_cmp_si(get_q(v), -1, 1) > 0))
		    set_d(ed_atanh_q(get_q(v)));
		else
		    set_dd(edd_atanh_q(get_q(v)));
		break;
	    case t_mpr:
		if (likely(!mpfr_nan_p(get_r(v)))) {
		    if ((mpfr_sgn(get_r(v)) >= 0 &&
			 mpfr_cmp_ui(get_r(v), 1) < 0) ||
			(mpfr_sgn(get_r(v)) < 0 &&
			 mpfr_cmp_si(get_r(v), -1) > 0)) {
			er_atanh_r(thr_rr, get_r(v));
			set_r(thr_rr);
		    }
		    else {
			ecc_atanh_r(thr_c, get_r(v));
			set_cc(thr_c);
		    }
		}
		else {
		    mpfr_set_nan(thr_rr);
		    set_r(thr_rr);
		}
		break;
	    case t_cdd:
		set_dd(edd_atanh_dd(get_dd(v)));
		break;
	    case t_cqq:
		set_dd(edd_atanh_qq(get_qq(v)));
		break;
	    case t_mpc:
		ecc_atanh_cc(thr_c, get_cc(v));
		set_cc(thr_c);
		break;
	    default:
		eraise_exception(except_not_a_number);
	}
    }
}

static void
vo_atanh_vo__r(vo_t *u, vo_t *v, eint32_t m)
{
    for (; m; u++, v++, m--) {
	switch (eobject_type(*v)) {
	    case t_void:
		er_atanh_i(thr_rr, 0);
		set_r(thr_rr);
		break;
	    case t_int:
		if (get_i(v) > -1 && get_i(v) < 1) {
		    er_atanh_i(thr_rr, get_i(v));
		    set_r(thr_rr);
		}
		else {
		    ecc_atanh_i(thr_c, get_i(v));
		    set_cc(thr_c);
		}
		break;
	    case t_float:
		if (likely(!isnan(get_d(v)))) {
		    if (fabs(get_d(v)) < 1.0)
			set_d(ed_atanh_d(get_d(v)));
		    else
			set_dd(edd_atanh_d(get_d(v)));
		}
		else
		    set_d(NAN);
		break;
	    case t_mpz:
		ecc_atanh_z(thr_c, get_z(v));
		set_cc(thr_c);
		break;
	    case t_mpq:
		if ((mpq_sgn(get_q(v)) >= 0 &&
		     mpq_cmp_ui(get_q(v), 1, 1) < 0) ||
		    (mpq_sgn(get_q(v)) < 0 &&
		     mpq_cmp_si(get_q(v), -1, 1) > 0)) {
		    er_atanh_q(thr_rr, get_q(v));
		    set_r(thr_rr);
		}
		else {
		    ecc_atanh_q(thr_c, get_q(v));
		    set_cc(thr_c);
		}
		break;
	    case t_mpr:
		if (likely(!mpfr_nan_p(get_r(v)))) {
		    if ((mpfr_sgn(get_r(v)) >= 0 &&
			 mpfr_cmp_ui(get_r(v), 1) < 0) ||
			(mpfr_sgn(get_r(v)) < 0 &&
			 mpfr_cmp_si(get_r(v), -1) > 0)) {
			er_atanh_r(thr_rr, get_r(v));
			set_r(thr_rr);
		    }
		    else {
			ecc_atanh_r(thr_c, get_r(v));
			set_cc(thr_c);
		    }
		}
		else {
		    mpfr_set_nan(thr_rr);
		    set_r(thr_rr);
		}
		break;
	    case t_cdd:
		set_dd(edd_atanh_dd(get_dd(v)));
		break;
	    case t_cqq:
		ecc_atanh_qq(thr_c, get_qq(v));
		set_cc(thr_c);
		break;
	    case t_mpc:
		ecc_atanh_cc(thr_c, get_cc(v));
		set_cc(thr_c);
		break;
	    default:
		eraise_exception(except_not_a_number);
	}
    }
}
#endif
